/**
 * 
 */
package info.niwota.frames.web;

import info.niwota.commons.web.AjaxRegistry;
import info.niwota.commons.web.PageCursor;
import info.niwota.commons.web.PageCursor.Type;
import info.niwota.frames.Debug;
import info.niwota.frames.FrameHelper;
import info.niwota.frames.ImageFrame;
import info.niwota.frames.ImageUtils;
import info.niwota.frames.Util;
import info.niwota.webapp.WebUtils;
import info.niwota.webapp.web.PageContentProvider;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.json.JSONObject;

import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

/**
 * @author qiangli
 */
public class FramePageContentProvider extends PageContentProvider {
	public static final String AUTHORITY = "info.niwota.frames";

	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
			+ "/");

	public static final String BASE_URI = "/content/" + AUTHORITY;

	private static final String TAG = "PageContentProvider";

	private static final String MAIN_PAGE = "index-galleria.html";

	public static Uri constructUri(Uri uri) {
		Uri u = Uri.withAppendedPath(CONTENT_URI, uri.getEncodedPath());
		if (Debug.DEBUG) {
			Log.d(TAG, " " + u);
		}
		return u;
	}

	@Override
	public boolean onCreate() {
		super.onCreate();
		//
		registerObjects();
		return true;
	}

	private void registerObjects() {
		final Context ctx = getContext();
		AjaxRegistry.getInstance().add(FrameObject.NAME, new FrameObject(ctx));
	}

	private void showFrames(PageCursor c, Uri uri) throws Exception {
		//handleAsset(c, MAIN_PAGE, "text/html");
		JSONObject data = new JSONObject();
		String peer = uri.getQueryParameter("peer");
		data.put("peer", NVL(peer, ""));
		handleAssetData(c, MAIN_PAGE, "text/html", data);
	}

	protected void handleImage(PageCursor c, String u, int group, String user)
			throws Exception {
		Uri uri = Uri.parse(u);
		if (Debug.DEBUG) {
			Log.d(TAG, "handleImage uri: " + uri);
		}

		// checkHash(uri, group, user);

		File f = getDataFile(uri);

		showImage(c, f, false);
	}

	private File getDataFile(Uri uri) {
		final String scheme = uri.getScheme();
		//
		String path = null;
		if ("file".equals(scheme)) {
			path = uri.getPath();
		} else {
			path = Util.getDataPath(getContext(), uri);
		}
		File f = new File(path);
		return f;
	}

	@Override
	protected boolean showContent(PageCursor c, Uri uri, int group,
			String user, String path) throws Exception {
		if (path.equals("/wallpaper")) {
			//Context ctx = getContext();
			//File f = BackgroundImages.loadBG(ctx);
			//showImage(c, f, true);
			
			///TODO
			return false;
		}
		
		if (path.equals("/bin/help")) {
			handleAsset(c, "help.html", "text/html");
			return true;
		}

		//  /bin/image
		if (showImageContent(c, uri, group, user, path)) {
			return true;
		}

		//  /bin/zip batch image
		if (showZipContent(c, uri, group, user, path)) {
			return true;
		}

		// /bin/frames
		showFrames(c, uri);
		return true;
	}

	//test /content/info.niwota.frames/bin/zip?label=Pictures-0&batch=0&size=thumbnail
	private boolean showZipContent(PageCursor c, Uri uri, int group,
			String user, String path) throws Exception {
		if (!path.equals("/bin/zip")) {
			return false;
		}
		
		//TODO
		//checkHash(uri, group, user);

		final String label = URLDecoder.decode(uri.getQueryParameter("label"));

		final String size = uri.getQueryParameter("size");
		
		//remove support for arbitrary width/height for now
		//int width = parseInt(uri.getQueryParameter("width"), -1);
		//int height = parseInt(uri.getQueryParameter("height"), -1);

		final int batch = parseInt(uri.getQueryParameter("batch"), 0);

		if (Debug.DEBUG) {
			Log.d(TAG, "path: " + path + " label: " + label + " size: "
					+ size + " batch: " + batch);
		}

		String prefix = null;
		
		//boolean original = (size != null && size.equals("original"));
		boolean thumb = (size != null && size.equals("thumbnail"));
		if (thumb) {
			prefix = "/thumb";
		} else {
			prefix = "/vignette";
		}
		
		File zipfile = getCacheFile("/zipped/" + prefix + "/" + FrameHelper.encodeLabel(label) + "/" + batch);

		final Context ctx = getContext();

		File store = FrameHelper.getLocalStore(ctx, label);
		
		if (zipfile.exists() && store.lastModified() < zipfile.lastModified()) {
			if (Debug.DEBUG) {
				Log.d(TAG, "frame zip cache file up to date: " + zipfile +" lastModified: " + new java.util.Date(zipfile.lastModified()));
			}
		} else {
			//get list of files from frame
			ImageFrame frame = FrameHelper.loadLocalFrame(ctx, label);
			try {
				zip(frame, zipfile, batch, size, thumb);
			} catch (Exception e) {
				zipfile.delete(); //clean up if failed
				
				throw e;
			}
		}

		showFile(c, zipfile, APPLICATION_ZIP);
		return true;
	}

	protected long parseLong(String s, long def) {
		try {
			return Long.parseLong(s);
		} catch (Exception e) {
			//
		}
		return def;
	}
	/**
	 * Zip thumbnail with pos as file name
	 * 
	 * @param frame
	 * @param zipfile
	 * @param batch
	 * @param prefix
	 * @param width
	 * @param height
	 * @throws Exception
	 */
	private void zip(ImageFrame frame, File zipfile, int batch, String prefix, boolean thumb) throws Exception {
		if (Debug.DEBUG) {
			Log.d(TAG, "zipping frame: " + frame + " batch: " + batch);
		}
		List<Uri> uris = frame.getReferences();
		final int total = (uris == null ? 0 : uris.size());

		final int batchSize = ImageFrame.BATCH_SIZE;
		final int bufSize = 1024;
		
		byte[] data = new byte[bufSize];
		
		ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
				new FileOutputStream(zipfile)));
		try {
			int begin = batch * batchSize;
			int end = Math.min(total, begin + batchSize);
			for (int pos = begin; pos < end; pos++) {
				Uri imageUri = uris.get(pos);
				File image = getDataFile(imageUri);
				//
				ZipEntry ze = new ZipEntry("" + pos);
				ze.setComment(imageUri.toString());
				out.putNextEntry(ze);
				
				if (image.exists()) {
					File newImage = getCacheFile(prefix + image.getPath());
					
					if (thumb) {
						ImageUtils.thumbnail(image, newImage);	
					} else {
						ImageUtils.vignette(image, newImage);
					}
					
					BufferedInputStream in = null;
					try {
						in = new BufferedInputStream(new FileInputStream(newImage), bufSize);
						int count;
						while ((count = in.read(data, 0, bufSize)) != -1) {
							out.write(data, 0, count);
						}
					} finally {
						in.close();
					}
				}
				
				out.closeEntry();
			}
		} finally {
			out.flush();
			out.close();
		}
	}

	private boolean showImageContent(PageCursor c, Uri uri, int group,
			String user, String path) throws Exception {
		if (!path.equals("/bin/image")) {
			return false;
		}

		final boolean data = Boolean.parseBoolean(uri.getQueryParameter("data"));
		final String size = uri.getQueryParameter("size");
		
		final Uri imgUri = Uri.parse(URLDecoder.decode(uri.getQueryParameter("uri")));
		
		if (Debug.DEBUG) {
			Log.d(TAG, "path: " + path + " image uri: " + imgUri + " data: "
					+ data + " size: " + size);
		}

		boolean thumb = (size != null && size.equals("thumbnail"));

		boolean isPeer = !WebUtils.isLocal(uri);
		
		if (isPeer) {
			Context ctx = getContext();
			String peer = uri.getQueryParameter("peer");
			String label = URLDecoder.decode(uri.getQueryParameter("label"));
			long modified = parseLong(uri.getQueryParameter("peer"), 0);
			File file = null;
			if (thumb) {
				int pos = parseInt(uri.getQueryParameter("pos"), -1);
				if (pos == -1) {
					throw new Exception("invalid pos " + uri);
				}
				file = FrameHelper.getCachedPeerThumbnail(ctx, peer, label, pos);
			} else {
				String hash = uri.getQueryParameter("hash");
				Uri cu = FrameHelper.buildPeerImageContentUri(peer, label, hash, imgUri.toString());
				file = FrameHelper.getCachedPeerImage(ctx, cu, modified);
			}
	
			showImage(c, file, data);
			return true;
		}
		
		// checkHash(imageUri, group, user);

		File image = getDataFile(imgUri);

		if (!image.exists()) {
			throw new IOException("Not found: " + imgUri);
		}
		//
		boolean original = TextUtils.isEmpty(size) || size.equals("original");

		String prefix = null;
		if (original) {
			prefix = "/image_original";
		} else if (thumb) {
			prefix = "/thumb";
		} else if (!TextUtils.isEmpty(size)) {
			prefix = "/vignette";
		}

		File newImage = getCacheFile(prefix + image.getPath());
		//
		File file = (original ? image : transformImage(image, newImage, thumb));

		showImage(c, file, data);

		return true;
	}

	private int parseInt(String s, int def) {
		try {
			return Integer.parseInt(s);
		} catch (Exception e) {
			//
		}
		return def;
	}

	private File transformImage(File image, File newImage,  boolean thumb)
			throws Exception {
		if (Debug.DEBUG) {
			Log.d(TAG, "image file: " + image + " new: " + newImage +" thumb: " + thumb);
		}

		if (!newImage.exists()
				|| newImage.lastModified() < image.lastModified()) {
			try {
				if (thumb) {
					ImageUtils.thumbnail(image, newImage);
				} else {
					ImageUtils.vignette(image, newImage);
				}
			} catch (Exception e) {

				newImage.delete();
				
				throw e;
			}
		}

		return newImage;
	}

	private void showImage(PageCursor c, File f, boolean data) throws Exception {
		if (data) {
			c.type = Type.URI;
			c.contentType = IMAGE_ANY;
			c.contentLength = f.length();
			c.uri = f.toURI().toString();
		} else {
			JSONObject jo = new JSONObject();
			jo.put("path", f.getPath());
			jo.put("length", f.length());
			jo.put("lastModified", f.lastModified());
			//
			showJson(c, jo.toString());
		}
	}

//	private void transform(File image, File thumb, int width, int height)
//			throws IOException {
//	
////		InputStream in = null;
////		OutputStream out = null;
////		try {
////			in = new FileInputStream(image);
////			out = new FileOutputStream(thumb);
////			// BackgroundImages.thumbnail(in, out, width, height);
////			//BackgroundImages.transform(in, out, width, height);
////		} finally {
////			in.close();
////			out.close();
////		}
//	}

	@Override
	protected void showMainPage(PageCursor c, Uri uri, int group, String user,
			String path) throws Exception {
		showFrames(c, uri);
	}
}
